#include "myshell.h"

//全局定义命令行参数表
char* gargv[ARGS] = {NULL};
int gargc = 0;

//用于存储环境变量PWD
char pwd[1024];

//用于存储上一个子进程的退出码
int lastcode;

void Debug()
{
    printf("hello shell!\n");
}

void InitGlobal()
{
    gargc = 0;
    memset(gargv, 0, sizeof(gargv));
}

static std::string GetUserName()
{
    string username = getenv("USER");
    return username.empty() ? "None" : username;
}

static std::string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

static std::string GetPwd()
{
    //string pwd = getenv("PWD");
    //return pwd.empty() ? "None" : pwd;
    char temp[1024];
    getcwd(temp, sizeof(temp));
    //顺便更新一下自己shell的环境变量
    snprintf(pwd, sizeof(pwd), "PWD=%s", temp);
    putenv(pwd);

    //命令行提示符中输出单个路径
    std::string pwd_label = temp;
    const std::string pathsep = "/"; //路径分隔符
    //查找长路径中最后一个'/'的位置
    size_t pos = pwd_label.rfind(pathsep);
    if(pos == std::string::npos)
    {
        //整个路径都没有'/',返回None
        return "None";
    }
    //从pos位置的下一个位置开始截取，相当于跳过pathsep截取后续子串
    pwd_label = pwd_label.substr(pos + pathsep.size());

    //如果此时size为0说明什么都没截取到，说明截取前pwd_label中只有"/"
    //则返回"/"
    return pwd_label.size() ? pwd_label : "/";
}

static std::string GetHomePath()
{
    std:string home = getenv("HOME");
    //若环境变量缺失或被篡改home为空，为空则回退到家目录
    return home.empty() ? "/" : home;
}

void PrintCommandPrompt()
{
    std::string username = GetUserName();
    std::string hostname = GetHostName();
    std::string pwd = GetPwd();
    printf("[%s@%s %s]# ", username.c_str(), hostname.c_str(), pwd.c_str());
} 

bool GetCommandString(char cmdstr_buff[], int len)
{
    if(cmdstr_buff == NULL || len <= 0)
    {
        //参数不合法
        return false;
    }
    char* res = fgets(cmdstr_buff, len, stdin);
    if(res == NULL)
    {
        //读取字符串失败
        return false;
    }
    //把输入的回车也就是'\n'置为'\0'
    cmdstr_buff[strlen(cmdstr_buff) - 1] = 0;
    return strlen(cmdstr_buff) == 0 ? false : true; 
}

bool ParseCommandString(char cmd[])
{
    if(cmd == NULL)
    {
        //安全检查
        return false;
    }
    //可以在函数内部定义，SEP表示分隔符
#define SEP " "
    //"ls -a -l" -> "ls" "-a" "-l" 
    
    //把第一个子串写入gargv[0],然后gargc++
    gargv[gargc++] = strtok(cmd, SEP);
    //把子串全部写入gargv数组里，并且以NULL结尾
    while(gargv[gargc++] = strtok(NULL, SEP))
        ;//循环空语句
    //回退一次命令行参数的个数
    --gargc;
    
    //条件编译，测试代码
    //#define DEBUG
    #ifdef DEBUG

    printf("gargc: %d\n", gargc);
    printf("--------------------------\n");
    for(int i = 0; i < gargc; i++)
    {
        printf("gargv[%d]: %s\n", i, gargv[i]);
    }
    printf("--------------------------\n");
    for(int i = 0; gargv[i]; i++)
    {
        printf("gargv[%d]: %s\n", i, gargv[i]);
    }

    #endif

    return true;
}

void ForkAndExec()
{
    pid_t id = fork();
    if(id < 0)
    {
        //fork失败
        perror("fork");
        return;
    }
    else if(id == 0)
    {
        //子进程
        execvp(gargv[0], gargv);
        exit(0);
    }
    else{
        //父进程
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
        {
            //获取子进程退出码
            lastcode = WEXITSTATUS(status);
        }
    }

}

bool BuildInCommandExec()
{
    //不能：gargv[0] == "cd" 这样比
    //这样比是比较指针是否相同，而非字符内容
    std::string cmd = gargv[0];
    bool ret = false; //默认不是内建命令
    if(cmd == "cd")//这里"cd"会被隐式类型转换为string
    {
        if(gargc == 2)
        {
            std::string target = gargv[1];
            if(target == "~")
            {
                //"cd ~"返回家目录
                chdir(GetHomePath().c_str());   
                lastcode = 0;
                ret = true;
            }
            else{
                chdir(gargv[1]);
                lastcode = 0;
                ret = true;
            }
        }
        else if(gargc == 1)
        {
            chdir(GetHomePath().c_str());
            lastcode = 0;
            ret = true;
        }
        else{
            //错误
        }
    }
    else if(cmd == "echo")
    {
        if(gargc == 2)
        {
            std::string args = gargv[1];
            if(args[0] == '$')
            {
                if(args[1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                    ret = true; 
                }
                else{
                    const char* name = &args[1];           
                    printf("%s\n", getenv(name));
                    lastcode = 0;
                    ret = true;
                }
            }
            else{
                printf("%s\n", gargv[1]);
                ret = true;
            }
        }
    }

    return ret;
}













